package com.ruben.utils.green;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.green.model.v20180509.ImageSyncScanRequest;
import com.aliyuncs.green.model.v20180509.TextScanRequest;
import com.aliyuncs.green.model.v20180509.VideoAsyncScanRequest;
import com.aliyuncs.green.model.v20180509.VideoAsyncScanResultsRequest;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.HttpResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.ruben.utils.AjaxJson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.DependsOn;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName: GreenUtils
 * @Description:
 * @Date: 2020/8/8 15:06
 * *
 * @author: achao<achao1441470436 @ gmail.com>
 * @version: 1.0
 * @since: JDK 1.8
 */
@Slf4j
@Component
@DependsOn("aliyunConfig")
public class GreenUtils {

    /**
     * 异步视频回调地址http/https
     */
    private static final String VIDEO_CALLBACK_URL = "http://vampireachao.utools.club/green/videoCallback";
    /**
     * 异步视频回调携带的签名
     */
    private static final String SEED = "签名";
    /**
     * 往最贵的整！
     * porn：图片智能鉴黄
     * terrorism：图片暴恐涉政识别
     * ad：图文违规识别
     * qrcode：图片二维码识别
     * live：图片不良场景识别
     * logo：图片logo识别
     */
    List<String> picScenes = Arrays.asList("porn", "terrorism", "ad", "live", "logo", "qrcode");
    List<String> videoScenes = Arrays.asList("porn", "terrorism", "ad", "live", "logo");
    List<String> textScenes = Collections.singletonList("antispam");
    @Resource
    private IAcsClient iAcsClient;

    private AjaxJson dealWithResponse(HttpResponse httpResponse) {
        AjaxJson ajaxJson = new AjaxJson();
        List<List<GreenResult>> totalGreenResultList = new ArrayList<>();
        List<GreenResult> greenResultList = new ArrayList<>();
        //服务端接收到请求，完成处理后返回的结果。
        if (httpResponse != null && httpResponse.isSuccess()) {
            JSONObject scrResponse = JSON.parseObject(org.apache.commons.codec.binary.StringUtils.newStringUtf8(httpResponse.getHttpContent()));
            System.out.println(JSON.toJSONString(scrResponse, true));
            int requestCode = scrResponse.getIntValue("code");
            //每一task的检测结果。
            JSONArray taskResults = scrResponse.getJSONArray("data");
            if (HttpStatus.OK.value() == requestCode) {
                for (Object taskResult : taskResults) {
                    //单task的处理结果。
                    int taskCode = ((JSONObject) taskResult).getIntValue("code");
                    //对应检测场景的处理结果。如果是多个场景，则会有每个场景的结果。
                    JSONArray sceneResults = ((JSONObject) taskResult).getJSONArray("results");
                    if (HttpStatus.OK.value() == taskCode) {
                        for (Object sceneResult : sceneResults) {
                            String scene = ((JSONObject) sceneResult).getString("scene");
                            String suggestion = ((JSONObject) sceneResult).getString("suggestion");
                            String label = ((JSONObject) sceneResult).getString("label");
                            //根据scene和suggetion做相关处理。
                            //do something
                            System.out.println("scene = [" + scene + "]");
                            System.out.println("suggestion = [" + suggestion + "]");
                            GreenResult greenResult = new GreenResult();
                            greenResult.setScene(scene);
                            greenResult.setSuggestion(suggestion);
                            greenResult.setLabel(label);
                            greenResultList.add(greenResult);
                        }
                        totalGreenResultList.add(greenResultList);
                    } else {
                        //单task处理失败, 原因视具体的情况详细分析。
                        log.warn("task process fail. task response:" + JSON.toJSONString(taskResult));
                        //ajaxJson.setMsg("task process fail. task response:" + ((JSONObject) scrResponse.getJSONArray("data").get(0)).getString("msg"));
                        ajaxJson.setMsg(codeTranslate(taskCode));
                        ajaxJson.setCode(taskCode);
                        ajaxJson.setSuccess(false);
                    }
                }
            } else {
                /**
                 * 表明请求整体处理失败，原因视具体的情况详细分析。
                 */
                log.error("the whole image scan request failed. response:" + JSON.toJSONString(scrResponse));
                return AjaxJson.error("请求失败").put("data", codeTranslate(scrResponse.getInteger("code")));
            }
        } else {
            return AjaxJson.error("请求失败");
        }
        ajaxJson.put("data", totalGreenResultList);
        return ajaxJson;
    }

    /**
     * 图片鉴黄
     *
     * @param urls
     * @return
     */
    public AjaxJson greenPic(String... urls) {
        if (urls.length == 0) {
            return AjaxJson.error("请传入url");
        }
        ImageSyncScanRequest imageSyncScanRequest = new ImageSyncScanRequest();
        // 指定API返回格式。
        imageSyncScanRequest.setAcceptFormat(FormatType.JSON);
        // 指定请求方法。
        imageSyncScanRequest.setMethod(MethodType.POST);
        imageSyncScanRequest.setEncoding("utf-8");
        //支持HTTP和HTTPS。
        imageSyncScanRequest.setProtocol(ProtocolType.HTTP);

        JSONObject httpBody = new JSONObject();
        /**
         * 设置要检测的风险场景。计费依据此处传递的场景计算。
         * 一次请求中可以同时检测多张图片，每张图片可以同时检测多个风险场景，计费按照场景计算。
         * 例如：检测2张图片，场景传递porn和terrorism，计费会按照2张图片鉴黄，2张图片暴恐检测计算。
         * porn：表示鉴黄场景。
         */
        httpBody.put("scenes", picScenes);
        /**
         * 设置待检测图片。一张图片对应一个task。
         * 多张图片同时检测时，处理的时间由最后一个处理完的图片决定。
         * 通常情况下批量检测的平均响应时间比单张检测的要长。一次批量提交的图片数越多，响应时间被拉长的概率越高。
         * 这里以单张图片检测作为示例, 如果是批量图片检测，请自行构建多个task。
         */
        List<JSONObject> tasks = Arrays.stream(urls).map(url -> {
            JSONObject task = new JSONObject();
            task.put("dataId", UUID.randomUUID().toString());
            //设置图片链接。
            task.put("url", url);
            task.put("time", new Date());
            return task;
        }).collect(Collectors.toList());
        httpBody.put("tasks", tasks);

        imageSyncScanRequest.setHttpContent(org.apache.commons.codec.binary.StringUtils.getBytesUtf8(httpBody.toJSONString()),
                "UTF-8", FormatType.JSON);

        /**
         * 请设置超时时间。服务端全链路处理超时时间为10秒，请做相应设置。
         * 如果您设置的ReadTimeout小于服务端处理的时间，程序中会获得一个read timeout异常。
         */
        imageSyncScanRequest.setConnectTimeout(3000);
        imageSyncScanRequest.setReadTimeout(10000);
        HttpResponse httpResponse = null;
        try {
            httpResponse = iAcsClient.doAction(imageSyncScanRequest);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dealWithResponse(httpResponse);
    }

    /**
     * 视频鉴黄
     *
     * @param urls
     * @return
     */
    public AjaxJson greenVideo(String... urls) {
        if (urls.length == 0) {
            return AjaxJson.error("请传入url");
        }
        VideoAsyncScanRequest videoAsyncScanRequest = new VideoAsyncScanRequest();
        // 指定API返回格式。
        videoAsyncScanRequest.setAcceptFormat(FormatType.JSON);
        // 指定请求方法。
        videoAsyncScanRequest.setMethod(com.aliyuncs.http.MethodType.POST);

        List<Map<String, Object>> tasks = Arrays.stream(urls).map(url -> {
            JSONObject task = new JSONObject();
            task.put("dataId", UUID.randomUUID().toString());
            //设置链接
            task.put("url", url);
            task.put("time", new Date());
            return task;
        }).collect(Collectors.toList());

        /**
         * 设置要检测的场景。计费是依据此处传递的场景计算。
         * 视频默认1秒截取一帧，您可以自行控制截帧频率。收费按照视频的截帧数量以及每一帧的检测场景计算。
         * 举例：1分钟的视频截帧60张，检测色情（对应场景参数porn）和暴恐涉政（对应场景参数terrorism）2个场景，收费按照60张暴恐+60张暴恐涉政进行计费。
         */
        JSONObject data = new JSONObject();
        data.put("scenes", videoScenes);
        data.put("tasks", tasks);
        //回调地址http/https，为空则定时查询结果
        data.put("callback", VIDEO_CALLBACK_URL);
        //回调通知请求中的签名
        data.put("seed", SEED);
        videoAsyncScanRequest.setHttpContent(data.toJSONString().getBytes(StandardCharsets.UTF_8), "UTF-8", FormatType.JSON);

        /**
         * 请务必设置超时时间。
         */
        videoAsyncScanRequest.setConnectTimeout(3000);
        videoAsyncScanRequest.setReadTimeout(6000);
        try {
            HttpResponse httpResponse = iAcsClient.doAction(videoAsyncScanRequest);
            if (httpResponse.isSuccess()) {
                JSONObject jsonObject = JSON.parseObject(new String(httpResponse.getHttpContent(), StandardCharsets.UTF_8));
                System.out.println(JSON.toJSONString(jsonObject, true));
                int requestCode = jsonObject.getIntValue("code");
                JSONArray taskResults = jsonObject.getJSONArray("data");
                if (HttpStatus.OK.value() == requestCode) {
                    for (Object taskResult : taskResults) {
                        int taskCode = ((JSONObject) taskResult).getIntValue("code");
                        if (HttpStatus.OK.value() == taskCode) {
                            String taskId = ((JSONObject) taskResult).getString("taskId");
                            String dataId = ((JSONObject) taskResult).getString("dataId");
                            //存taskId、dataId和对应的审核
                            return AjaxJson.success("视频异步提交成功！");
                        } else {
                            return AjaxJson.error(codeTranslate(taskCode));
                        }
                    }
                } else {
                    log.error("task process fail. task response:" + ((JSONObject) jsonObject.getJSONArray("data").get(0)).getString("msg"));
                    return AjaxJson.error(codeTranslate(requestCode));
                }
            } else {
                log.error("response not success. status:" + httpResponse.getStatus());
                return AjaxJson.error(codeTranslate(httpResponse.getStatus()));
            }
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return AjaxJson.error("视频异步提交失败");
    }

    public AjaxJson checkVideo(String taskId) {
        VideoAsyncScanResultsRequest videoAsyncScanResultsRequest = new VideoAsyncScanResultsRequest();
        videoAsyncScanResultsRequest.setAcceptFormat(FormatType.JSON);
        List<String> taskList = new ArrayList<String>();
        // 这里添加要查询的taskId。提交任务的时候需要自行保存taskId。
        taskList.add(taskId);
        videoAsyncScanResultsRequest.setHttpContent(JSON.toJSONString(taskList).getBytes(StandardCharsets.UTF_8), "UTF-8", FormatType.JSON);

        /**
         * 请务必设置超时时间。
         */
        videoAsyncScanResultsRequest.setConnectTimeout(3000);
        videoAsyncScanResultsRequest.setReadTimeout(6000);
        HttpResponse httpResponse = null;
        try {
            httpResponse = iAcsClient.doAction(videoAsyncScanResultsRequest);
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return dealWithResponse(httpResponse);
    }

    /**
     * 文本反垃圾
     *
     * @param texts
     * @return
     */
    public AjaxJson greenText(String... texts) {
        if (texts.length == 0) {
            return AjaxJson.error("请传入待检测的文字");
        }
        TextScanRequest textScanRequest = new TextScanRequest();
        // 指定API返回格式。
        textScanRequest.setAcceptFormat(FormatType.JSON);
        // 指定请求方法。
        textScanRequest.setHttpContentType(FormatType.JSON);
        textScanRequest.setMethod(com.aliyuncs.http.MethodType.POST);
        textScanRequest.setEncoding("UTF-8");
        textScanRequest.setRegionId("cn-shanghai");
        List<Map<String, Object>> tasks = Arrays.stream(texts).map(text -> {
            Map<String, Object> task = new LinkedHashMap<String, Object>();
            task.put("dataId", UUID.randomUUID().toString());
            /**
             * 待检测的文本，长度不超过10000个字符。
             */
            task.put("content", text);
            return task;
        }).collect(Collectors.toList());


        JSONObject data = new JSONObject();

        /**
         * 检测场景。文本垃圾检测请传递antispam。
         **/
        data.put("scenes", textScenes);
        data.put("tasks", tasks);
        System.out.println(JSON.toJSONString(data, true));
        textScanRequest.setHttpContent(data.toJSONString().getBytes(StandardCharsets.UTF_8), "UTF-8", FormatType.JSON);
        // 请务必设置超时时间。
        textScanRequest.setConnectTimeout(3000);
        textScanRequest.setReadTimeout(6000);
        HttpResponse httpResponse = null;
        try {
            httpResponse = iAcsClient.doAction(textScanRequest);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dealWithResponse(httpResponse);
    }

    /**
     * 状态码翻译，累死我了
     *
     * @param code
     * @return
     */
    public String codeTranslate(Integer code) {
        String result = "";
        switch (code) {
            case 200:
                result = "请求成功";
                break;
            case 280:
                result = "任务正在执行中，建议您等待一段时间（例如5s）后再查询结果";
                break;
            case 400:
                result = "请求有误，通常由于请求参数不正确导致，请仔细检查请求参数";
                break;
            case 401:
                result = "请求失败，通常是由于使用了不安全的图片、视频、语音链接地址";
                break;
            case 403:
                result = "请求访问失败，通常由于您的图片、视频、语音链接无法访问导致，请确认公网是否可访问，并且无防盗链策略";
                break;
            case 404:
                result = "待检测内容未找到，通常是由于您的图片、视频、语音内容无法下载导致，请确认内容可通过公网访问到";
                break;
            case 480:
                result = "下载失败，请确认待检测内容的大小、分辨率（如果有）在API的限制范围内";
                break;
            case 500:
                result = "服务端临时出错。建议重试，若持续返回该错误码，请通过工单联系我们";
                break;
            case 580:
                result = "数据库操作失败。建议重试，若持续返回该错误码，请通过工单联系我们";
                break;
            case 581:
                result = "超时。建议重试，若持续返回该错误码，请通过工单联系我们";
                break;
            case 585:
                result = "缓存出错。建议重试，若持续返回该错误码，请通过工单联系我们";
                break;
            case 586:
                result = "算法出错。请通过工单联系我们";
                break;
            case 587:
                result = "中间件出错。请通过工单联系我们";
                break;
            case 588:
                result = "请求频率超出配额。默认配额：图片检测50张/秒，视频检测20路/秒，语音检测20路/秒，文本检测100条/秒。如果需要调整配额，请通过工单联系我们";
                break;
            case 589:
                result = "待检测内容过大，请确保检测的内容在API的限制范围内。建议重试，若持续返回该错误码，请通过工单联系我们";
                break;
            case 590:
                result = "待检测内容格式错误，请确保检测的内容在API的限制范围内";
                break;
            case 591:
                result = "连接池满。请通过工单联系我们";
                break;
            case 592:
                result = "下载超时，下载时间限制为3s，请确保检测的内容大小在API的限制范围内";
                break;
            case 594:
                result = "任务过期，如taskId过期";
                break;
            case 595:
                result = "截帧失败，请通过工单联系我们";
                break;
            case 596:
                result = "账号未授权、账号欠费、账号未开通、账号被禁等原因，具体可以参考返回的msg";
                break;
            default:
                break;
        }
        return result;
    }

}
